package org.jsets.fastboot.security.authz;

import java.util.List;
import org.jsets.fastboot.security.IAuthzRuleProvider;
import org.jsets.fastboot.security.SecurityUtils;
import org.jsets.fastboot.util.JsonUtils;
import org.jsets.fastboot.util.SpringContextHolder;
import org.jsets.fastboot.util.StringUtils;
import org.jsets.fastboot.util.WebUtils;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class AuthzRuleManager {

	private final Object monitor = new Object();
	private final List<AuthzRule> RULES = Lists.newArrayList();
	private IAuthzRuleProvider authzRuleProvider;
	private IAuthzRuleMatcher authzRuleMatcher;

	public void afterInitialize() {
		IAuthzRuleProvider provider = SpringContextHolder.tryBean(IAuthzRuleProvider.class);
		if (null != provider) {
			this.authzRuleProvider = provider;
			this.refreshAuthzRules();
		}

		IAuthzRuleMatcher matcher = SpringContextHolder.tryBean(IAuthzRuleMatcher.class);
		if (null != matcher) {
			this.authzRuleMatcher = matcher;
		}
	}

	public AuthzRule getAuthzRule(String subsystem, String httpMethod, String servletPath) {
		boolean matchSubsystem = StringUtils.notEmpty(subsystem);
		int size = RULES.size();
		if (null == this.authzRuleMatcher) {
			for (int i = 0; i < size; i++) {
				AuthzRule rule = RULES.get(i);
				if (matchSubsystem) {
					if (StringUtils.isEmpty(rule.getSubsystem())) {
						continue;
					}
					if (!subsystem.equals(rule.getSubsystem())) {
						continue;
					}
				}

				if (StringUtils.notEmpty(rule.getMethod())) {
					if (!httpMethod.equalsIgnoreCase(rule.getMethod())) {
						continue;
					}
				}

				String pattern = rule.getPath();
				if ("GET".equalsIgnoreCase(httpMethod)) {
					pattern = pattern + "/**";
				}
				if (WebUtils.pathMatch(pattern, servletPath)) {
					return rule;
				}
			}
		} else {
			for (int i = 0; i < size; i++) {
				AuthzRule rule = RULES.get(i);
				if (this.authzRuleMatcher.match(subsystem, httpMethod, servletPath, rule)) {
					return rule;
				}
			}
		}
		
		return null;
	}

	public void refreshAuthzRules() {
		if (null != this.authzRuleProvider) {
			synchronized (monitor) {
				RULES.clear();
				this.authzRuleProvider.loadAuthzRuleList(SecurityUtils.getProperties().getSubsystemName())
						.forEach(t -> {
							RULES.add(t);
							log.info("AuthzRule[{}]", JsonUtils.toJson(t));
						});
			}
		}
	}

	public IAuthzRuleProvider getAuthzRuleProvider() {
		return authzRuleProvider;
	}

	public IAuthzRuleMatcher getAuthzRuleMatcher() {
		return authzRuleMatcher;
	}

}
